From: Florian Müllner Date: Tue, 12 Jul 2011 19:47:37 +0000 (+0200) Subject: popup-menu: Add support for child menus X-Git-Tag: archive/raspbian/50.5-1+rpi1~1^2~1013^2^2~7856 X-Git-Url: https://dgit.raspbian.org/%22http://www.example.com/cgi/%22/%22http:/www.example.com/cgi/%22?a=commitdiff_plain;h=5d2b7e2c9e2de78455c815d2caafc0738250e292;p=gnome-shell.git popup-menu: Add support for child menus Allow opening a popup menu from another menu. While the child menu is open, events on the parent menu are blocked. The parent menu is kept open when the child menu is closed; the child menu on the other hand is closed with the parent, e.g. when the focus moves to another toplevel menu. This feature will be used to implement combo box menu items. https://bugzilla.gnome.org/show_bug.cgi?id=652837 --- diff --git a/js/ui/popupMenu.js b/js/ui/popupMenu.js index 7b28108de..ecc5ec1e5 100644 --- a/js/ui/popupMenu.js +++ b/js/ui/popupMenu.js @@ -800,6 +800,7 @@ PopupMenuBase.prototype = { this.passEvents = false; this._activeMenuItem = null; + this._childMenus = []; }, addAction: function(title, callback) { @@ -831,6 +832,28 @@ PopupMenuBase.prototype = { return menuItem; }, + isChildMenu: function(menu) { + return this._childMenus.indexOf(menu) != -1; + }, + + addChildMenu: function(menu) { + if (this.isChildMenu(menu)) + return; + + this._childMenus.push(menu); + this.emit('child-menu-added', menu); + }, + + removeChildMenu: function(menu) { + let index = this._childMenus.indexOf(menu); + + if (index == -1) + return; + + this._childMenus.splice(index, 1); + this.emit('child-menu-removed', menu); + }, + /** * _connectSubMenuSignals: * @object: a menu item, or a menu section @@ -1400,6 +1423,7 @@ PopupMenuManager.prototype = { this._keyFocusNotifyId = 0; this._activeMenu = null; this._menus = []; + this._menuStack = []; this._preGrabInputMode = null; this._grabbedFromKeynav = false; }, @@ -1408,6 +1432,8 @@ PopupMenuManager.prototype = { let menudata = { menu: menu, openStateChangeId: menu.connect('open-state-changed', Lang.bind(this, this._onMenuOpenState)), + childMenuAddedId: menu.connect('child-menu-added', Lang.bind(this, this._onChildMenuAdded)), + childMenuRemovedId: menu.connect('child-menu-removed', Lang.bind(this, this._onChildMenuRemoved)), destroyId: menu.connect('destroy', Lang.bind(this, this._onMenuDestroy)), enterId: 0, focusInId: 0 @@ -1435,6 +1461,8 @@ PopupMenuManager.prototype = { let menudata = this._menus[position]; menu.disconnect(menudata.openStateChangeId); + menu.disconnect(menudata.childMenuAddedId); + menu.disconnect(menudata.childMenuRemovedId); menu.disconnect(menudata.destroyId); if (menudata.enterId) @@ -1472,8 +1500,20 @@ PopupMenuManager.prototype = { }, _onMenuOpenState: function(menu, open) { - if (open) + if (open) { + if (this._activeMenu && this._activeMenu.isChildMenu(menu)) { + this._menuStack.push(this._activeMenu); + menu.actor.grab_key_focus(); + } this._activeMenu = menu; + } else { + if (this._menuStack.length > 0) { + this._activeMenu = this._menuStack.pop(); + if (menu.sourceActor) + menu.sourceActor.grab_key_focus(); + this._didPop = true; + } + } // Check what the focus was before calling pushModal/popModal let focus = global.stage.key_focus; @@ -1506,6 +1546,14 @@ PopupMenuManager.prototype = { } }, + _onChildMenuAdded: function(menu, childMenu) { + this.addMenu(childMenu); + }, + + _onChildMenuRemoved: function(menu, childMenu) { + this.removeMenu(childMenu); + }, + // change the currently-open menu without dropping grab _changeMenu: function(newMenu) { if (this._activeMenu) { @@ -1514,6 +1562,8 @@ PopupMenuManager.prototype = { // before closing it to keep that from happening let oldMenu = this._activeMenu; this._activeMenu = null; + for (let i = this._menuStack.length - 1; i >= 0; i--) + this._menuStack[i].close(false); oldMenu.close(false); newMenu.open(false); } else @@ -1524,6 +1574,15 @@ PopupMenuManager.prototype = { if (!this.grabbed || menu == this._activeMenu) return false; + if (this._activeMenu && this._activeMenu.isChildMenu(menu)) + return false; + + if (this._menuStack.indexOf(menu) != -1) + return false; + + if (this._menuStack.length > 0 && this._menuStack[0].isChildMenu(menu)) + return false; + this._changeMenu(menu); return false; }, @@ -1536,6 +1595,8 @@ PopupMenuManager.prototype = { if (focus) { if (this._activeMenuContains(focus)) return; + if (this._menuStack.length > 0) + return; if (focus._delegate && focus._delegate.menu && this._findMenu(focus._delegate.menu) != -1) return; @@ -1594,6 +1655,11 @@ PopupMenuManager.prototype = { if (this._activeMenu != null && this._activeMenu.passEvents) return false; + if (this._didPop) { + this._didPop = false; + return true; + } + let activeMenuContains = this._eventIsOnActiveMenu(event); let eventType = event.type();